package com.example.demo.graph;

import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.KeyStrategy;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.action.AsyncEdgeAction;
import com.alibaba.cloud.ai.graph.action.AsyncNodeAction;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.List;

import static com.alibaba.cloud.ai.graph.StateGraph.END;
import static com.alibaba.cloud.ai.graph.StateGraph.START;
import com.alibaba.cloud.ai.graph.node.code.CodeExecutorNodeAction;
import com.alibaba.cloud.ai.graph.node.code.entity.CodeExecutionConfig;
import com.alibaba.cloud.ai.graph.node.code.CodeExecutor;
import com.alibaba.cloud.ai.graph.node.code.LocalCommandlineCodeExecutor;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Collectors;
import com.alibaba.cloud.ai.graph.node.AnswerNode;
import com.alibaba.cloud.ai.graph.node.KnowledgeRetrievalNode;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.ai.document.Document;
import com.alibaba.cloud.ai.graph.node.VariableAggregatorNode;
import com.alibaba.cloud.ai.graph.node.AssignerNode;
import com.alibaba.cloud.ai.graph.node.HttpNode;
import org.springframework.http.HttpMethod;
import com.alibaba.cloud.ai.graph.node.QuestionClassifierNode;
import static com.alibaba.cloud.ai.graph.action.AsyncEdgeAction.edge_async;
import com.alibaba.cloud.ai.graph.node.LlmNode;
import org.springframework.ai.chat.messages.AssistantMessage;
import com.alibaba.cloud.ai.graph.node.BranchNode;

@Component
public class GraphBuilder {
    // todo: add your knowledge base
    @Value("${rag.source:classpath:/data/}")
    private Resource ragSource;

    @Value("${app.api.base-url}")
    private String baseUrl;
    @Bean
    public CompiledGraph buildGraph(
        ChatModel chatModel
        , VectorStore vectorStore
        , CodeExecutionConfig codeExecutionConfig, CodeExecutor codeExecutor
        ) throws Exception {
        ChatClient chatClient = ChatClient.builder(chatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();

        // new stateGraph
        // todo: Add some non-node variable names, such as sys and conversation variables. Format as sys_xxx.
        StateGraph stateGraph = new StateGraph(() -> {
  Map<String, KeyStrategy> strategies = new HashMap<>();
  strategies.put("sys_query", (o1, o2) -> o2);
  strategies.put("knowledgeRetrievalNode2_result", (o1, o2) -> o2);
strategies.put("LLMNode5_text", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode3_result", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode1_result", (o1, o2) -> o2);
strategies.put("codeNode12_otherInfo", (o1, o2) -> o2);
strategies.put("httpNode9_status_code", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode4_result", (o1, o2) -> o2);
strategies.put("codeNode18_result", (o1, o2) -> o2);
strategies.put("codeNode11_conclusion", (o1, o2) -> o2);
strategies.put("httpNode9_files", (o1, o2) -> o2);
strategies.put("httpNode6_status_code", (o1, o2) -> o2);
strategies.put("codeNode1_character_design", (o1, o2) -> o2);
strategies.put("codeNode3_general_rules", (o1, o2) -> o2);
strategies.put("httpNode6_files", (o1, o2) -> o2);
strategies.put("startNode1_lessee", (o1, o2) -> o2);
strategies.put("httpNode7_status_code", (o1, o2) -> o2);
strategies.put("codeNode6_customer_analysis_str", (o1, o2) -> o2);
strategies.put("codeNode14_stopHosting", (o1, o2) -> o2);
strategies.put("httpNode8_status_code", (o1, o2) -> o2);
strategies.put("codeNode3_general_rules_str", (o1, o2) -> o2);
strategies.put("httpNode9_headers", (o1, o2) -> o2);
strategies.put("LLMNode13_text", (o1, o2) -> o2);
strategies.put("httpNode3_body", (o1, o2) -> o2);
strategies.put("codeNode4_filtered_product_str", (o1, o2) -> o2);
strategies.put("codeNode14_id", (o1, o2) -> o2);
strategies.put("httpNode3_files", (o1, o2) -> o2);
strategies.put("LLMNode6_text", (o1, o2) -> o2);
strategies.put("LLMNode15_text", (o1, o2) -> o2);
strategies.put("httpNode5_body", (o1, o2) -> o2);
strategies.put("httpNode2_body", (o1, o2) -> o2);
strategies.put("codeNode11_totalScore", (o1, o2) -> o2);
strategies.put("httpNode6_headers", (o1, o2) -> o2);
strategies.put("image_analysis", (o1, o2) -> o2);
strategies.put("LLMNode12_text", (o1, o2) -> o2);
strategies.put("httpNode2_headers", (o1, o2) -> o2);
strategies.put("startNode1_message_id", (o1, o2) -> o2);
strategies.put("codeNode13_result", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode9_result", (o1, o2) -> o2);
strategies.put("httpNode1_files", (o1, o2) -> o2);
strategies.put("codeNode20_result", (o1, o2) -> o2);
strategies.put("httpNode3_status_code", (o1, o2) -> o2);
strategies.put("httpNode1_status_code", (o1, o2) -> o2);
strategies.put("codeNode2_special_scenarios_str", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode7_result", (o1, o2) -> o2);
strategies.put("codeNode4_product_names_list", (o1, o2) -> o2);
strategies.put("httpNode2_status_code", (o1, o2) -> o2);
strategies.put("codeNode17_result", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode5_result", (o1, o2) -> o2);
strategies.put("codeNode6_custom_info", (o1, o2) -> o2);
strategies.put("httpNode5_status_code", (o1, o2) -> o2);
strategies.put("httpNode4_status_code", (o1, o2) -> o2);
strategies.put("startNode1_server_conversation_id", (o1, o2) -> o2);
strategies.put("codeNode8_collect_product_info_str", (o1, o2) -> o2);
strategies.put("httpNode4_files", (o1, o2) -> o2);
strategies.put("codeNode17_links", (o1, o2) -> o2);
strategies.put("LLMNode8_text", (o1, o2) -> o2);
strategies.put("codeNode7_result", (o1, o2) -> o2);
strategies.put("LLMNode10_text", (o1, o2) -> o2);
strategies.put("LLMNode2_text", (o1, o2) -> o2);
strategies.put("codeNode5_lessee_tags_str", (o1, o2) -> o2);
strategies.put("LLMNode3_text", (o1, o2) -> o2);
strategies.put("codeNode19_description", (o1, o2) -> o2);
strategies.put("LLMNode16_text", (o1, o2) -> o2);
strategies.put("collect_product_info", (o1, o2) -> o2);
strategies.put("collect_product_info_str", (o1, o2) -> o2);
strategies.put("httpNode6_body", (o1, o2) -> o2);
strategies.put("LLMNode9_text", (o1, o2) -> o2);
strategies.put("startNode1_customer_name", (o1, o2) -> o2);
strategies.put("httpNode5_files", (o1, o2) -> o2);
strategies.put("httpNode7_headers", (o1, o2) -> o2);
strategies.put("codeNode17_count", (o1, o2) -> o2);
strategies.put("codeNode2_special_scenarios", (o1, o2) -> o2);
strategies.put("codeNode16_result", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode6_result", (o1, o2) -> o2);
strategies.put("httpNode3_headers", (o1, o2) -> o2);
strategies.put("codeNode11_productScore", (o1, o2) -> o2);
strategies.put("codeNode18_count", (o1, o2) -> o2);
strategies.put("codeNode10_total_score", (o1, o2) -> o2);
strategies.put("httpNode9_body", (o1, o2) -> o2);
strategies.put("answerNode2_answer", (o1, o2) -> o2);
strategies.put("answerNode1_answer", (o1, o2) -> o2);
strategies.put("LLMNode19_text", (o1, o2) -> o2);
strategies.put("codeNode18_links", (o1, o2) -> o2);
strategies.put("LLMNode18_text", (o1, o2) -> o2);
strategies.put("httpNode10_headers", (o1, o2) -> o2);
strategies.put("codeNode11_round", (o1, o2) -> o2);
strategies.put("httpNode8_body", (o1, o2) -> o2);
strategies.put("codeNode1_character_design_str", (o1, o2) -> o2);
strategies.put("startNode1_history", (o1, o2) -> o2);
strategies.put("LLMNode20_text", (o1, o2) -> o2);
strategies.put("httpNode10_files", (o1, o2) -> o2);
strategies.put("startNode1_source", (o1, o2) -> o2);
strategies.put("codeNode15_quotableProductNames", (o1, o2) -> o2);
strategies.put("codeNode4_product_names_str", (o1, o2) -> o2);
strategies.put("httpNode7_files", (o1, o2) -> o2);
strategies.put("questionClassifyNode1_class_name", (o1, o2) -> o2);
strategies.put("variableAggregatorNode1_output", (o1, o2) -> o2);
strategies.put("stop_hosting", (o1, o2) -> o2);
strategies.put("codeNode6_user_tags_str", (o1, o2) -> o2);
strategies.put("httpNode4_headers", (o1, o2) -> o2);
strategies.put("codeNode11_roundScore", (o1, o2) -> o2);
strategies.put("codeNode14_reason", (o1, o2) -> o2);
strategies.put("codeNode9_result", (o1, o2) -> o2);
strategies.put("codeNode5_lessee_tags", (o1, o2) -> o2);
strategies.put("codeNode4_filtered_product_data", (o1, o2) -> o2);
strategies.put("httpNode8_headers", (o1, o2) -> o2);
strategies.put("httpNode10_status_code", (o1, o2) -> o2);
strategies.put("sceneId", (o1, o2) -> o2);
strategies.put("httpNode8_files", (o1, o2) -> o2);
strategies.put("httpNode5_headers", (o1, o2) -> o2);
strategies.put("codeNode8_collect_product_info", (o1, o2) -> o2);
strategies.put("LLMNode7_text", (o1, o2) -> o2);
strategies.put("httpNode10_body", (o1, o2) -> o2);
strategies.put("httpNode1_headers", (o1, o2) -> o2);
strategies.put("codeNode12_customerInfo", (o1, o2) -> o2);
strategies.put("httpNode7_body", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode8_result", (o1, o2) -> o2);
strategies.put("httpNode4_body", (o1, o2) -> o2);
strategies.put("httpNode2_files", (o1, o2) -> o2);
strategies.put("LLMNode17_text", (o1, o2) -> o2);
strategies.put("LLMNode4_text", (o1, o2) -> o2);
strategies.put("LLMNode1_text", (o1, o2) -> o2);
strategies.put("LLMNode14_text", (o1, o2) -> o2);
strategies.put("LLMNode11_text", (o1, o2) -> o2);
strategies.put("codeNode10_result", (o1, o2) -> o2);
strategies.put("httpNode1_body", (o1, o2) -> o2);
strategies.put("variableAggregatorNode2_output", (o1, o2) -> o2);
strategies.put("codeNode12_customerDemand", (o1, o2) -> o2);
  return strategies;
}
);
        // add nodes
        // —— HttpNode [17557641927460] ——
HttpNode httpNode1 = HttpNode.builder()
.method(HttpMethod.POST)
.url(baseUrl +"/lessee/inner/roleSetting/get")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-3362\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n  \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n  \\\"lesseeId\\\": \\\"${startNode1_lessee}\\\"\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode1_body")
.build();
stateGraph.addNode("httpNode1", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode1";
   String key = varName + "_body";
   Map<String, Object> result = httpNode1.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— HttpNode [17557656415320] ——
HttpNode httpNode2 = HttpNode.builder()
.method(HttpMethod.POST)
.url(baseUrl +"/notice/inner/sceneList")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-3513\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n  \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n  \\\"lesseeId\\\": ${startNode1_lessee}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode2_body")
.build();
stateGraph.addNode("httpNode2", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode2";
   String key = varName + "_body";
   Map<String, Object> result = httpNode2.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1755767729755] ——
CodeExecutorNodeAction codeNode1 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {"character_design": {}, "character_design_str": "暂无角色设定"}

    try:
        response_data = json.loads(arg1)
    except json.JSONDecodeError:
        return {
            "character_design": {},
            "character_design_str": "输入的角色设定不是合法的 JSON",
        }

    data = response_data.get("data", {}) if isinstance(response_data, dict) else {}

    template = (
        "你是{name}，性别{sex}，使用的语言是{chatLang}。"
        "你的交流风格是{chatStyle}，并且精通{skill}。"
        "你现在正在作为{companyName}的销售人员，与客户沟通，该公司的主营产品包括{mainProducts}。"
        "目标客户群体主要是{customerGroup}。"
        "在对话中，你需要保持自然、亲切，同时展现专业度。"
        "回答要生动、有趣，并适当地融入{skill}文化元素，让客户感受到轻松和信任。"
        "在理解客户需求的基础上，自然地推荐合适的产品，强调这些产品如何符合客户的兴趣和风格。"
    )

    data_str = template.format(
        name=data.get("name", "未命名角色"),
        sex=data.get("sex", "未知"),
        skill=data.get("skill", "暂无技能"),
        companyName=data.get("companyName", "某公司"),
        mainProducts=data.get("mainProducts", "相关产品"),
        customerGroup=data.get("customerGroup", "客户群体"),
        chatStyle=data.get("chatStyle", "自然随和"),
        chatLang=data.get("chatLang", "英文"),
    )

    return {"character_design": data, "character_design_str": data_str}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode1_body"))
.outputKey("codeNode1_output")
    .build();
stateGraph.addNode("codeNode1", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode1.apply(state);
            String key = "codeNode1_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode1_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [1755767751197] ——
CodeExecutorNodeAction codeNode2 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {"special_scenarios": [], "special_scenarios_str": "暂无特殊场景"}

    try:
        response_data = json.loads(arg1)
    except json.JSONDecodeError:
        return {
            "special_scenarios": [],
            "special_scenarios_str": "输入的内容不是合法的 JSON",
        }

    data = response_data.get("data", [])
    cleaned_data = [dict(item) for item in data if isinstance(item, dict)]

    data_str = (
        json.dumps(cleaned_data, ensure_ascii=False, indent=2)
        if cleaned_data
        else "暂无特殊场景"
    )

    return {"special_scenarios": cleaned_data, "special_scenarios_str": data_str}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode2_body"))
.outputKey("codeNode2_output")
    .build();
stateGraph.addNode("codeNode2", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode2.apply(state);
            String key = "codeNode2_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode2_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— HttpNode [17557683471520] ——
HttpNode httpNode3 = HttpNode.builder()
.url(baseUrl +"/api/sale-rule/query/"+ "1891605565158")
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode3_body")
.build();
stateGraph.addNode("httpNode3", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode3";
   String key = varName + "_body";
   Map<String, Object> result = httpNode3.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1755768706621] ——
CodeExecutorNodeAction codeNode3 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {"general_rules": [], "general_rules_str": "暂无通用规则"}

    try:
        response_data = json.loads(arg1)
    except json.JSONDecodeError:
        return {"general_rules": [], "general_rules_str": "输入的内容不是合法的 JSON"}

    data = response_data.get("data", [])
    rules_list = [
        {"id": item.get("id", ""), "ruleContent": item.get("ruleContent", "")}
        for item in data
        if isinstance(item, dict)
    ]

    rules_text = (
        "\\n".join(
            f"{i + 1}. {item['ruleContent']}" for i, item in enumerate(rules_list)
        )
        if rules_list
        else "暂无通用规则"
    )

    return {"general_rules": rules_list, "general_rules_str": rules_text}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode3_body"))
.outputKey("codeNode3_output")
    .build();
stateGraph.addNode("codeNode3", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode3.apply(state);
            String key = "codeNode3_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode3_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— HttpNode [17557763591460] ——
HttpNode httpNode4 = HttpNode.builder()
.url(baseUrl +"/api/sale-plan/query/all")
.queryParam("lesseeId", "${startNode1_lessee}")
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode4_body")
.build();
stateGraph.addNode("httpNode4", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode4";
   String key = varName + "_body";
   Map<String, Object> result = httpNode4.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1755776428456] ——
CodeExecutorNodeAction codeNode4 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {
            "filtered_product_data": [],
            "product_names_list": [],
            "product_names_str": "",
            "filtered_product_str": "",
        }

    data = json.loads(arg1).get("data", [])
    filtered_output, product_names_list, filtered_product_list = [], [], []

    for product in data:
        product_name = product.get("productName", "")
        attributes = product.get("attributes", [])

        filtered_attrs = []
        for attr in attributes:
            label = attr.get("label", "")
            raw_desc = attr.get("description", "")
            option_text = f"（可选的值有{raw_desc}）" if raw_desc else ""

            description_text = f"现在客户想要【{product_name}】，请询问客户对于【{product_name}】的【{label}】需求{option_text}"

            filtered_attrs.append(
                {
                    "label": label,
                    "description": description_text,
                    "score": attr.get("score", 10),
                    "value": attr.get("value", ""),
                    "reason": "",
                }
            )

        if filtered_attrs:
            filtered_output.append(
                {
                    "productName": product_name,
                    "thresholdScore": product.get("thresholdScore", 50),
                    "totalScore": product.get("totalScore"),
                    "isQuotable": product.get("isQuotable", 0),
                    "attributes": filtered_attrs,
                }
            )

        if product_name:
            product_names_list.append(product_name)

        simple_attrs = [
            {"label": attr.get("label", ""), "description": attr.get("description", "")}
            for attr in attributes
        ]
        if simple_attrs:
            filtered_product_list.append(
                {"productName": product_name, "attributes": simple_attrs}
            )

    return {
        "filtered_product_data": filtered_output,
        "product_names_list": product_names_list,
        "product_names_str": json.dumps(product_names_list, ensure_ascii=False)
        if product_names_list
        else "",
        "filtered_product_str": json.dumps(filtered_product_list, ensure_ascii=False)
        if filtered_product_list
        else "",
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode4_body"))
.outputKey("codeNode4_output")
    .build();
stateGraph.addNode("codeNode4", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode4.apply(state);
            String key = "codeNode4_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode4_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— HttpNode [17558307127980] ——
HttpNode httpNode5 = HttpNode.builder()
.method(HttpMethod.POST)
.url(baseUrl +"/lessee/inner/lesseeTags/get")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-4065\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n    \\\"apiKey\\\": ${env_app_key},\\n    \\\"lesseeId\\\": ${startNode1_lessee}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode5_body")
.build();
stateGraph.addNode("httpNode5", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode5";
   String key = varName + "_body";
   Map<String, Object> result = httpNode5.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [17558307242790] ——
CodeExecutorNodeAction codeNode5 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {"lessee_tags": [], "lessee_tags_str": "[]"}

    try:
        response_data = json.loads(arg1)
    except json.JSONDecodeError:
        return {"lessee_tags": [], "lessee_tags_str": "[]"}

    data = response_data.get("data", [])
    lessee_tags = [
        {"tagId": item.get("tagId", ""), "tagName": item.get("tagName", "")}
        for item in data
        if isinstance(item, dict)
    ]

    lessee_tags_str = json.dumps(lessee_tags, ensure_ascii=False)

    return {"lessee_tags": lessee_tags, "lessee_tags_str": lessee_tags_str}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode5_body"))
.outputKey("codeNode5_output")
    .build();
stateGraph.addNode("codeNode5", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode5.apply(state);
            String key = "codeNode5_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode5_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— BranchNode [1755849694052] ——
stateGraph.addNode("branchNode1", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1755850441014] ——
LlmNode LLMNode1 = LlmNode.builder()
.systemPromptTemplate("角色设定：{codeNode1_character_design_str}\n任务是判断用户消息是否触发特殊场景规则。\n\n输入：\n- 特殊场景：{codeNode2_special_scenarios_str}\n- 商家在售商品：{codeNode4_filtered_product_str}\n\n规则：\n1. 最外层必须是一个对象，包含唯一字段 triggeredScenes。\n2. 如果没有触发，返回 {\"triggeredScenes\": []}。\n3. 禁止输出用户消息内容或其他文字，只输出 JSON。")
.userPromptTemplate("{sys_query}")
.params(Map.of("sys_query", "null", "codeNode2_special_scenarios_str", "null", "codeNode1_character_design_str", "null", "codeNode4_filtered_product_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode1_text")
.build();
stateGraph.addNode("LLMNode1", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode1.apply(state);
	String key = "LLMNode1_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— HttpNode [1755853472970] ——
HttpNode httpNode6 = HttpNode.builder()
.method(HttpMethod.POST)
.url(baseUrl +"/notice/inner/pushAlertMsg")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-109\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n  \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n  \\\"sceneId\\\": ${conversation_sceneId},\\n  \\\"lesseeId\\\": ${startNode1_lessee},\\n  \\\"imUserId\\\": ${sys_user_id},\\n  \\\"msgId\\\": ${startNode1_message_id},\\n  \\\"conversationId\\\": \\\"${startNode1_server_conversation_id}\\\",\\n  \\\"query\\\": \\\"${sys_query}\\\",\\n  \\\"message\\\": \\\"${variableAggregatorNode1_output}\\\",\\n  \\\"reason\\\": \\\"${codeNode14_reason}\\\"\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode6_body")
.build();
stateGraph.addNode("httpNode6", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode6";
   String key = varName + "_body";
   Map<String, Object> result = httpNode6.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— LlmNode [1755854546432] ——
LlmNode LLMNode2 = LlmNode.builder()
.systemPromptTemplate("你是一个智能助手，任务是根据输入信息生成客户简要画像。\n\n⚠️ 规则（必须遵守）：\n1. 输出必须严格是一个 JSON 数组。\n2. 数组中只包含一个对象。\n3. 对象必须包含以下三个字段：customerInfo、customerDemand、otherInfo。\n4. 输出中禁止出现任何非 JSON 内容（包括自然语言解释、注释或额外文字）。\n\n输入信息：\n- 客户之前的总结分析：{codeNode6_customer_analysis_str}\n- 客户需求商品：{conversation_collect_product_info_str}\n- 客户和销售的聊天记录：{{#context#}}\n\n✅ 输出示例（注意：只能输出这种 JSON 结构，不能有多余文字）：\n[\n  {\n    \"customerInfo\": \"客户为年轻职场新人，追求高性价比和实用性\",\n    \"customerDemand\": \"需要购买一款入门级智能手机\",\n    \"otherInfo\": \"客户比较关注价格和售后服务\"\n  }\n]")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode6_customer_analysis_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode2_text")
.build();
stateGraph.addNode("LLMNode2", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode2.apply(state);
	String key = "LLMNode2_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— HttpNode [17558556503520] ——
HttpNode httpNode7 = HttpNode.builder()
.method(HttpMethod.POST)
.url(baseUrl +"/lessee/inner/customInfos/get")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-8343\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n    \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n    \\\"lesseeId\\\": ${startNode1_lessee},\\n    \\\"imUserId\\\": ${sys_user_id}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode7_body")
.build();
stateGraph.addNode("httpNode7", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode7";
   String key = varName + "_body";
   Map<String, Object> result = httpNode7.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— HttpNode [1755856286673] ——
HttpNode httpNode8 = HttpNode.builder()
.method(HttpMethod.POST)
.url(baseUrl +"/lessee/inner/customerAnalyze/set")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-1500\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n  \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n  \\\"lesseeId\\\": ${startNode1_lessee},\\n  \\\"imUserId\\\": ${sys_user_id},\\n  \\\"customerInfo\\\": ${codeNode12_customerInfo},\\n  \\\"customerDemand\\\": ${codeNode12_customerDemand},\\n  \\\"otherInfo\\\": ${codeNode12_otherInfo}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode8_body")
.build();
stateGraph.addNode("httpNode8", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode8";
   String key = varName + "_body";
   Map<String, Object> result = httpNode8.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— LlmNode [1755856306126] ——
LlmNode LLMNode3 = LlmNode.builder()
.systemPromptTemplate("你是一个智能助手，任务是为客户打标签。\n\n输入：\n- 客户需求商品：{conversation_collect_product_info_str}\n- 可选标签列表：{codeNode5_lessee_tags_str}\n- 客户已有标签：{codeNode6_user_tags_str}\n\n规则：\n1. 只能从“可选标签列表”中选择标签，禁止生成新标签。\n2. 输出结果必须为 JSON，对象中必须有一个字段 \"tags_list\"。\n3. \"tags_list\" 的值是数组，数组内的每个对象必须包含 \"tagId\" 字段。\n4. 如果没有新增标签，输出 `{ \"tags_list\": [] }`。\n5. 严禁输出任何 JSON 以外的内容（包括解释、自然语言、注释）。\n\n输出格式示例：\n{\n  \"tags_list\": [\n    { \"tagId\": \"12345\" },\n    { \"tagId\": \"67890\" }\n  ]\n}")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode5_lessee_tags_str", "null", "codeNode6_user_tags_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode3_text")
.build();
stateGraph.addNode("LLMNode3", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode3.apply(state);
	String key = "LLMNode3_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— HttpNode [1755857078513] ——
HttpNode httpNode9 = HttpNode.builder()
.method(HttpMethod.POST)
.url(baseUrl +"/lessee/inner/customTags/set")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-9063\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n    \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n    \\\"imUserId\\\": ${sys_user_id},\\n    \\\"tagIds\\\": ${codeNode7_result}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode9_body")
.build();
stateGraph.addNode("httpNode9", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode9";
   String key = varName + "_body";
   Map<String, Object> result = httpNode9.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1755916796748] ——
CodeExecutorNodeAction codeNode6 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    try:
        data = json.loads(arg1)
    except json.JSONDecodeError:
        data = {}

    user_data = data.get("data", {})

    customer_fields = ["customerInfo", "customerDemand", "otherInfo"]
    customer_data = {k: user_data[k] for k in customer_fields if k in user_data}

    user_tags_str = json.dumps(user_data.get("tags", []), ensure_ascii=False)

    return {
        "custom_info": user_data,
        "customer_analysis_str": json.dumps(customer_data, ensure_ascii=False),
        "user_tags_str": user_tags_str,
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode7_body"))
.outputKey("codeNode6_output")
    .build();
stateGraph.addNode("codeNode6", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode6.apply(state);
            String key = "codeNode6_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode6_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [1755942129724] ——
CodeExecutorNodeAction codeNode7 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: dict) -> dict:
    return {"result": json.dumps(arg1)}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode3_structured_output"))
.outputKey("codeNode7_output")
    .build();
stateGraph.addNode("codeNode7", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode7.apply(state);
            String key = "codeNode7_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode7_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [1755945926794] ——
CodeExecutorNodeAction codeNode8 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
import copy

def main(arg1: list, arg2: list[dict], arg3: list[dict]):
    product_map = {
        item["productName"]: item
        for item in arg2
        if isinstance(item, dict) and "productName" in item
    }

    updated_arg3 = copy.deepcopy(arg3) if arg3 else []

    for i, item in enumerate(updated_arg3):
        product_name = item.get("product")
        ref_product = product_map.get(product_name)
        if not ref_product:
            continue

        ref_attrs_map = {a["label"]: a for a in ref_product.get("attributes", [])}
        new_attrs = []
        for attr in item.get("attributes", []):
            label = attr.get("label")
            if label not in ref_attrs_map:
                continue

            ref_attr = ref_attrs_map[label]
            merged_attr = attr.copy()

            for key in ["label", "description", "score"]:
                merged_attr[key] = ref_attr.get(key, merged_attr.get(key))

            merged_attr["reason"] = merged_attr.get("reason") or ref_attr.get(
                "reason", ""
            )

            merged_attr["value"] = merged_attr.get("value", "")

            new_attrs.append(merged_attr)

        updated_arg3[i]["attributes"] = new_attrs

    if arg1:
        collected_products = {item.get("product") for item in updated_arg3}
        for product_name in arg1:
            product_name = product_name.strip()
            if product_name in collected_products:
                continue
            ref_product = product_map.get(product_name)
            if not ref_product:
                continue

            new_attrs = []
            for attr in ref_product.get("attributes", []):
                attr_copy = attr.copy()
                attr_copy["value"] = ""
                attr_copy["reason"] = ""
                attr_copy["score"] = attr_copy.get("score", 10)
                new_attrs.append(attr_copy)

            updated_arg3.append(
                {
                    "product": product_name,
                    "thresholdScore": ref_product.get("thresholdScore", 50),
                    "totalScore": ref_product.get("totalScore", 50),
                    "isQuotable": ref_product.get("isQuotable", 0),
                    "attributes": new_attrs,
                }
            )

    return {
        "collect_product_info": updated_arg3,
        "collect_product_info_str": json.dumps(updated_arg3, ensure_ascii=False),
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode5_structured_output", "arg2", "codeNode4_filtered_product_data", "arg3", "conversation_collect_product_info"))
.outputKey("codeNode8_output")
    .build();
stateGraph.addNode("codeNode8", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode8.apply(state);
            String key = "codeNode8_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode8_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— LlmNode [1755947932458] ——
LlmNode LLMNode4 = LlmNode.builder()
.systemPromptTemplate("这是客户可能需要的商品信息：\n{codeNode8_collect_product_info_str}\n这是用户可能上传的图片信息：\n{conversation_image_analysis}\n\n规则：\n1. 禁止输出任何 JSON 以外的内容，只输出 JSON。\n2. 如果商品信息为空，请输出空数组：\n   {\"product_information\": []}\n3. 从上下文中提取商品属性填入对应 value：\n   - 找到对应值则填入 value\n   - 未找到则 value 为 \"\"\n   - 给出判断理由，填入reason字段\n4. 除了value和reason字段，其他字段应该保持不变。\n4. 输出 JSON 必须完全符合 Schema，禁止任何额外字段。\n5. 不允许自然语言回答，不允许中文说明或提示。")
.params(Map.of("codeNode8_collect_product_info_str", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode4_text")
.build();
stateGraph.addNode("LLMNode4", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode4.apply(state);
	String key = "LLMNode4_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— AssignerNode [1755948713597] ——
AssignerNode assignerNode1 = AssignerNode.builder()
.addItem("conversation_collect_product_info_str", "codeNode9_result", AssignerNode.WriteMode.OVER_WRITE)
.addItem("conversation_collect_product_info", "LLMNode4_structured_output", AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode1", AsyncNodeAction.node_async(assignerNode1));

// —— LlmNode [1755949477160] ——
LlmNode LLMNode5 = LlmNode.builder()
.systemPromptTemplate("你是智能助手，任务是判断用户是否对在售商品有潜在购买需求。\n\n输入：\n- 在售商品列表：{codeNode4_product_names_str}\n- 已收集需求商品：{conversation_collect_product_info_str}\n- 图片文本描述：{conversation_image_analysis}\n\n规则：\n1. 优先使用图片文本描述匹配在售商品（完全匹配或包含关键词）。\n2. 如果图片匹配不到，再使用已收集需求商品匹配在售商品。\n3. 只能从在售商品列表中选择，禁止生成新商品名称。\n5. 如果没有匹配到商品：\n   - new_products 输出 []\n   - purchase_intent 输出 \"无\"\n   - confidence 输出 0.0\n   - reason 输出简短说明\n6. **禁止输出任何 JSON 以外的内容，只输出 JSON。**")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode4_product_names_str", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode5_text")
.build();
stateGraph.addNode("LLMNode5", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode5.apply(state);
	String key = "LLMNode5_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— CodeNode [17560179385050] ——
CodeExecutorNodeAction codeNode9 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: dict) -> dict:
    if not arg1:
        return {"result": ""}
    return {
        "result": json.dumps(arg1, ensure_ascii=False),
    }

""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode4_structured_output"))
.outputKey("codeNode9_output")
    .build();
stateGraph.addNode("codeNode9", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode9.apply(state);
            String key = "codeNode9_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode9_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— LlmNode [1756018471542] ——
LlmNode LLMNode6 = LlmNode.builder()
.systemPromptTemplate("Role: {codeNode1_character_design_str}\n\nInputs:\nUser images: {conversation_image_analysis}\nReference scripts: {LLMNode14_text}\nInquiry attributes: {codeNode19_description}\nReference links: {LLMNode20_text}\n\n直接原则：\n回答要符合自然对话，保持简洁、保持自然的语气\n\n任务：\n1. 始终遵循参考脚本的风格和语气。\n2. 用“###”（不带###开头）分隔句子/链接。\n3. 推荐所有相关的参考链接；允许多个链接；每个链接以###结尾。\n\n禁止：\n- 句子不完整\n- 连续或重复的回答语句\n- 虚构的信息、链接、价格")
.params(Map.of("codeNode19_description", "null", "codeNode1_character_design_str", "null", "LLMNode14_text", "null", "LLMNode20_text", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode6_text")
.build();
stateGraph.addNode("LLMNode6", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode6.apply(state);
	String key = "LLMNode6_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— CodeNode [1756020347663] ——
CodeExecutorNodeAction codeNode10 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: list[dict], arg2: float, arg3: float, arg4: float, arg5: float) -> dict:
    results = []
    scores = []

    for product in arg1:
        product_score = 0
        filtered_attrs = []

        for attr in product.get("attributes", []):
            if attr.get("value"):
                product_score += attr.get("score", 0)
                filtered_attrs.append({k: v for k, v in attr.items() if k != "score"})

        if arg2 == arg3:
            product_score += arg4
        product_score += arg5

        threshold = product.get("thresholdScore", 0)
        if product_score >= threshold:
            results.append(
                {
                    "productName": product.get("productName"),
                    "attributes": filtered_attrs,
                }
            )
            scores.append(product_score)

    if not results:
        return {"result": [], "total_score": []}

    return {"result": results, "total_score": scores}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "conversation_collect_product_info", "arg2", "sys_dialogue_count", "arg3", "codeNode11_round", "arg4", "codeNode11_roundScore", "arg5", "codeNode11_productScore"))
.outputKey("codeNode10_output")
    .build();
stateGraph.addNode("codeNode10", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode10.apply(state);
            String key = "codeNode10_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode10_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— BranchNode [1756020912920] ——
stateGraph.addNode("branchNode2", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1756021890832] ——
LlmNode LLMNode7 = LlmNode.builder()
.systemPromptTemplate("请生成一条正式且温和的结束语：\n结束语模板：{codeNode11_conclusion}\n\n要求：\n1. 如果模板有值，用意思相近的英文表达；\n2. 如果模板为空，自由发挥，用英文表达。\n3. 内容意思为“我们正在处理您的请求，请耐心等待”，用词简洁自然，避免与常见表达完全雷同。")
.params(Map.of("codeNode11_conclusion", "null"))
.chatClient(chatClient)
.outputKey("LLMNode7_text")
.build();
stateGraph.addNode("LLMNode7", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode7.apply(state);
	String key = "LLMNode7_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— AnswerNode [1756021965125] ——
AnswerNode answerNode1 = AnswerNode.builder()
.answer("{LLMNode7_text}")
.outputKey("answerNode1_answer")
.build();
stateGraph.addNode("answerNode1", AsyncNodeAction.node_async(answerNode1));

stateGraph.addEdge("answerNode1", END);
// —— LlmNode [1756028965021] ——
LlmNode LLMNode8 = LlmNode.builder()
.systemPromptTemplate("You are an intelligent assistant. Your task is to generate a structured JSON description based on the user-uploaded image.\n\nInput:\n- User-uploaded image content (text description)\n- List of available product categories: {codeNode4_product_names_list}\n- Available product attributes: {codeNode4_filtered_product_str}\n\nRules:\n1. First, describe the image content in detail, including scene, objects, colors, actions, and other key elements.  \n2. Determine whether the image content is related to any available product category:  \n   - If related: output the image description + the corresponding product category.  \n   - If not related: output the image description and set the product category to \"No related product category\".  \n3. **Do not answer user questions or generate extra text. Only output JSON.**")
.userPromptTemplate("{sys_query}")
.params(Map.of("sys_query", "null", "codeNode4_product_names_list", "null", "codeNode4_filtered_product_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode8_text")
.build();
stateGraph.addNode("LLMNode8", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode8.apply(state);
	String key = "LLMNode8_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— LlmNode [1756029346700] ——
LlmNode LLMNode9 = LlmNode.builder()
.systemPromptTemplate("任务：根据商品信息与图片描述生成英文、单行、小写、**中横线分隔（kebab-case）**的搜索查询 slug，仅输出最终 slug，不要解释或多余内容。\n\n输入：\n- 商品信息：{conversation_collect_product_info_str}\n- 图片描述：{conversation_image_analysis}\n- 在售商品：{codeNode4_product_names_list}\n\n规则：\n1. 中文翻译为英文。\n2. 基础词取自 productName 核心名词，空格转中横线。\n3. 按 attributes 顺序追加非空 value（翻译为英文），单位规范化（如 \"2 inch\"→\"2inch\"、\"10 mm\"→\"10mm\"）。\n4. 可从图片描述提取 1–2 个英文关键词补充。\n5. 输出仅一条 slug，字符仅限 a–z、0–9、-。\n6. 若 attributes 值全为空或无商品信息，从在售商品随机选英文名称生成 slug。\n\n示例（仅演示格式，不限品类）：\n - 商品信息:\n{\"productName\": \"古巴链cuban chain\", \"attributes\": [{\"label\": \"尺寸-宽度（mm）\", \"description\": \"宽度（mm）\", \"value\": \"2\"}, {\"label\": \"尺寸-长度（inch）\", \"description\": \"长度（inch）\", \"value\": \"22\"}]}\n输出: cuban-chain-2mm-22inch\n - 商品信息:\n{\"productName\": \"玫瑰金定制字母Y吊坠\", \"attributes\": []}\n图片描述含 \"custom letter y pendant\"\n输出: rose-gold-letter-y-pendant\n - 商品信息:\n{\"productName\": \"红色运动鞋\", \"attributes\": [{\"label\": \"尺码\", \"description\": \"尺码\", \"value\": \"42\"}]}\n输出: sneakers-red-42\n - 商品信息:\n{\"productName\": \"实木餐桌\", \"attributes\": [{\"label\": \"尺寸-长度（米）\", \"description\": \"长度（米）\", \"value\": \"2\"}]}\n输出: wooden-dining-table-2m")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode4_product_names_list", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode9_text")
.build();
stateGraph.addNode("LLMNode9", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode9.apply(state);
	String key = "LLMNode9_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [1756029554923] ——
KnowledgeRetrievalNode knowledgeRetrievalNode1 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode9_text")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-large")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode1_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode1", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode1_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode1.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [1756030383240] ——
LlmNode LLMNode10 = LlmNode.builder()
.systemPromptTemplate("任务：根据商品信息和图片描述，生成中文关键词列表，使用“、”分隔，仅输出关键词，不含解释或多余内容。\n\n输入：\n - 商品信息：{conversation_collect_product_info_str}\n - 图片描述：{conversation_image_analysis}\n - 在售商品：{codeNode4_product_names_list}\n\n规则：\n1. 英文转中文。\n2. 从 productName 提取核心名词，空格转“、”。\n3. 按 attributes 顺序追加非空 value（中文翻译），单位转英文缩写（如 \"2 inch\"→\"2inch\"、\"10 mm\"→\"10mm\"）。\n4. 可从图片描述提取 1–2 个中文关键词补充。\n5. 输出仅一行关键词列表，使用“、”分隔。\n6. 若 attributes 值全为空或无商品信息，从在售商品随机选中文名称生成。\n\n示例（仅演示格式，不限品类）：\n- 商品信息: {\"productName\": \"古巴链cuban chain\", \"attributes\": [{\"label\": \"尺寸-宽度（mm）\", \"description\": \"宽度（mm）\", \"value\": \"2\"}, {\"label\": \"尺寸-长度（inch）\", \"description\": \"长度（inch）\", \"value\": \"22\"}]} \n输出: 古巴链、2mm、22inch\n- 商品信息: {\"productName\": \"玫瑰金定制字母Y吊坠\", \"attributes\": []} 图片描述含 \"custom letter y pendant\" \n输出: 玫瑰金、定制、字母吊坠、Yt\n- 商品信息: {\"productName\": \"红色运动鞋\", \"attributes\": [{\"label\": \"尺码\", \"description\": \"尺码\", \"value\": \"42\"}]} \n输出: 运动鞋、红色、42码\n- 商品信息: {\"productName\": \"实木餐桌\", \"attributes\": [{\"label\": \"尺寸-长度（米）\", \"description\": \"长度（米）\", \"value\": \"2\"}]} \n输出: 实木餐桌、2米")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode4_product_names_list", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode10_text")
.build();
stateGraph.addNode("LLMNode10", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode10.apply(state);
	String key = "LLMNode10_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [1756030505337] ——
KnowledgeRetrievalNode knowledgeRetrievalNode2 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode10_text")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-large")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode2_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode2", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode2_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode2.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [1756087090945] ——
LlmNode LLMNode11 = LlmNode.builder()
.systemPromptTemplate("根据上传图片{conversation_image_analysis}和客户意向商品{conversation_collect_product_info_str}，判断销售阶段（陌生、兴趣、信任、意向、订单、成交、复购），提炼客户关注的核心问题或词语，并生成向量检索字段。 禁止回答客户问题。 输出格式：阶段: <阶段> | 客户提问: {sys_query}")
.params(Map.of("conversation_collect_product_info_str", "null", "sys_query", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode11_text")
.build();
stateGraph.addNode("LLMNode11", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode11.apply(state);
	String key = "LLMNode11_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [1756087097420] ——
KnowledgeRetrievalNode knowledgeRetrievalNode3 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode11_text")
.topK(6)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-large")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.8)
.outputKey("knowledgeRetrievalNode3_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode3", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode3_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode3.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— HttpNode [1756089028563] ——
HttpNode httpNode10 = HttpNode.builder()
.url(baseUrl +"/api/mind/query/{{#1755760119759.lessee#}}")
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode10_body")
.build();
stateGraph.addNode("httpNode10", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode10";
   String key = varName + "_body";
   Map<String, Object> result = httpNode10.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1756089146690] ——
CodeExecutorNodeAction codeNode11 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    try:
        data = json.loads(arg1).get("data", {})
    except json.JSONDecodeError:
        data = {}

    result = {
        "totalScore": data.get("totalScore", 50),
        "round": data.get("round", 5),
        "roundScore": data.get("roundScore", 10),
        "productScore": data.get("productScore", 10),
        "conclusion": data.get("endStatement")
        or "I'm carefully handling it now. It may take just a moment, and I truly appreciate your patience.",
    }

    return result
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode10_body"))
.outputKey("codeNode11_output")
    .build();
stateGraph.addNode("codeNode11", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode11.apply(state);
            String key = "codeNode11_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode11_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— AssignerNode [1756104138927] ——
AssignerNode assignerNode2 = AssignerNode.builder()
.addItem("conversation_image_analysis", "LLMNode8_structured_output", AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode2", AsyncNodeAction.node_async(assignerNode2));

// —— CodeNode [1756114974542] ——
CodeExecutorNodeAction codeNode12 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: dict) -> dict:
    if not arg1:
        return {
            "customerInfo": "",
            "customerDemand": "",
            "otherInfo": ""
        }
    return {
        "customerInfo": arg1.get("customerInfo", ""),
        "customerDemand": arg1.get("customerDemand", ""),
        "otherInfo": arg1.get("otherInfo", "")
    }

""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode2_structured_output"))
.outputKey("codeNode12_output")
    .build();
stateGraph.addNode("codeNode12", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode12.apply(state);
            String key = "codeNode12_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode12_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— BranchNode [1756178976150] ——
stateGraph.addNode("branchNode3", AsyncNodeAction.node_async(state -> Map.of()));

// —— CodeNode [17562101662250] ——
CodeExecutorNodeAction codeNode13 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: list[dict]) -> dict:
    if not arg1:
        return {"result": ""}
    result_list = []
    for idx, item in enumerate(arg1, start=1):
        content = item.get("content", "").strip()
        score = item.get("metadata", {}).get("score", 0)

        item_str = f"{idx}. 内容: {content}; 相似度: {score:.3f}"
        result_list.append(item_str)

    result_str = "\\n".join(result_list)

    return {"result": result_str}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "knowledgeRetrievalNode3_result"))
.outputKey("codeNode13_output")
    .build();
stateGraph.addNode("codeNode13", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode13.apply(state);
            String key = "codeNode13_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode13_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [1756450218887] ——
CodeExecutorNodeAction codeNode14 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: list) -> dict:
    if not arg1 or not isinstance(arg1, list):
        return {
            "id": "",
            "stopHosting": "0",
            "reason": ""
        }

    first_item = arg1[0]
    return {
        "id": str(first_item.get("id", "")),
        "stopHosting": first_item.get("stopHosting", "0"),
        "reason": first_item.get("reason", "")
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode1_structured_output"))
.outputKey("codeNode14_output")
    .build();
stateGraph.addNode("codeNode14", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode14.apply(state);
            String key = "codeNode14_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode14_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— AssignerNode [1756451387126] ——
AssignerNode assignerNode3 = AssignerNode.builder()
.addItem("conversation_sceneId", null, AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode3", AsyncNodeAction.node_async(assignerNode3));

// —— BranchNode [1756454234375] ——
stateGraph.addNode("branchNode4", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1756870955241] ——
LlmNode LLMNode12 = LlmNode.builder()
.systemPromptTemplate("任务：根据商品信息与图片描述生成英文、单行、小写、**中横线分隔（kebab-case）**的搜索查询 slug，仅输出最终 slug，不要解释或多余内容。\n\n输入：\n- 商品信息：{conversation_collect_product_info_str}\n- 图片描述：{conversation_image_analysis}\n\n规则：\n1. 中文翻译为英文。\n2. 基础词取自 productName 核心名词，空格转中横线。\n3. 按 attributes 顺序追加非空 value（翻译为英文），单位规范化（如 \"2 inch\"→\"2inch\"、\"10 mm\"→\"10mm\"）。\n4. 可从图片描述提取 1–2 个英文关键词补充。\n5. 输出仅一条 slug，字符仅限 a–z、0–9、-。\n\n示例（仅演示格式，不限品类）：\n - 商品信息:\n{\"productName\": \"古巴链cuban chain\", \"attributes\": [{\"label\": \"尺寸-宽度（mm）\", \"description\": \"宽度（mm）\", \"value\": \"2\"}, {\"label\": \"尺寸-长度（inch）\", \"description\": \"长度（inch）\", \"value\": \"22\"}]}\n输出: cuban-chain-2mm-22inch\n - 商品信息:\n{\"productName\": \"玫瑰金定制字母Y吊坠\", \"attributes\": []}\n图片描述含 \"custom letter y pendant\"\n输出: rose-gold-letter-y-pendant\n - 商品信息:\n{\"productName\": \"红色运动鞋\", \"attributes\": [{\"label\": \"尺码\", \"description\": \"尺码\", \"value\": \"42\"}]}\n输出: sneakers-red-42\n - 商品信息:\n{\"productName\": \"实木餐桌\", \"attributes\": [{\"label\": \"尺寸-长度（米）\", \"description\": \"长度（米）\", \"value\": \"2\"}]}\n输出: wooden-dining-table-2m")
.params(Map.of("conversation_collect_product_info_str", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode12_text")
.build();
stateGraph.addNode("LLMNode12", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode12.apply(state);
	String key = "LLMNode12_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [1756871219944] ——
KnowledgeRetrievalNode knowledgeRetrievalNode4 = KnowledgeRetrievalNode.builder()
.inputKey("sys_query")
.topK(6)
.enableRanker(true)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-large")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(1.0)
.outputKey("knowledgeRetrievalNode4_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode4", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode4_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode4.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— KnowledgeRetrievalNode [1756871225455] ——
KnowledgeRetrievalNode knowledgeRetrievalNode5 = KnowledgeRetrievalNode.builder()
.inputKey("null")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-large")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode5_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode5", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode5_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode5.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [1756871378233] ——
LlmNode LLMNode13 = LlmNode.builder()
.systemPromptTemplate("You are a product quotation assistant. Based on customer info and product database, reply with an English quotation and product link.\n\nInput:\n- Collected product info: {conversation_collect_product_info_str}\n- Product database: {{#context#}}\n- Sales strategy: {codeNode13_result}\n- Search keywords: {structured_output.keywords}\n\nRequirements:\n1. Reply in English, ≤30 characters (spaces + punctuation). Links not counted.\n2. First sentence = core quotation.\n3. End each sentence with .?! and separate by ###.\n4. Only output database product links, each ending with ###.\n5. If similarity ≥40%, quote best match price + link. Else, give price range (low–high).\n6. Use highest similarity product attributes when quoting.\n7. Do not ask for provided attributes; only ask for missing ones.\n8. No repeated questions, no fabricated links, no outside products.")
.params(Map.of("codeNode13_result", "null", "conversation_collect_product_info_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode13_text")
.build();
stateGraph.addNode("LLMNode13", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode13.apply(state);
	String key = "LLMNode13_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [17573828773850] ——
KnowledgeRetrievalNode knowledgeRetrievalNode6 = KnowledgeRetrievalNode.builder()
.inputKey("sys_query")
.topK(10)
.enableRanker(true)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.7)
.outputKey("knowledgeRetrievalNode6_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode6", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode6_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode6.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— KnowledgeRetrievalNode [17573828917380] ——
KnowledgeRetrievalNode knowledgeRetrievalNode7 = KnowledgeRetrievalNode.builder()
.inputKey("sys_query")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.8)
.outputKey("knowledgeRetrievalNode7_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode7", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode7_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode7.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [1757382975020] ——
LlmNode LLMNode14 = LlmNode.builder()
.systemPromptTemplate("你是一个专业的【销售话术整合助手】。\n你的目标是：基于提供的销售思维与销售话术，结合客户提出的问题或需求，生成合适的回复，既要体现专业性，又要促成成交。\n输入信息：\nSOP销售阶段话术库：{codeNode13_result}\n销售话术：{{#context#}}\n商家规则：{codeNode3_general_rules_str}\n\n输出要求：\n融合“销售话术”中的内容，语言自然顺畅。\n输出的语气要：真诚、专业、引导成交。\n在满足客户疑问的同时，适度加入价值塑造和成交推进。\n每次回答需简洁有力（少于40字），避免啰嗦。")
.params(Map.of("codeNode13_result", "null", "codeNode3_general_rules_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode14_text")
.build();
stateGraph.addNode("LLMNode14", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode14.apply(state);
	String key = "LLMNode14_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— LlmNode [1757644567098] ——
LlmNode LLMNode15 = LlmNode.builder()
.systemPromptTemplate("你是一名高情商、专业且善于共情的心理倾听者，同时在销售场景中担任可信赖顾问。\n你的目标是与客户建立情感连接，取得客户信任，并在合适时推进销售。\n\n基本规则\n1. 回复内容必须使用英文。\n2. 优先共情，理解用户情绪、关注点和背景，适时鼓励、肯定或安慰。\n3. 根据用户话语判断当前状态：\n   - “继续共情”：用户仍在表达顾虑、情绪或犹豫，尚未显示购买意向。\n   - “推进销售”：用户表现出兴趣、购买意向或对你/你的建议好奇，愿意接受下一步安排。\n\n触发“推进销售”的信号包括：\n- 用户主动透露更多个人或背景信息\n- 用户主动询问你的意见或建议\n- 用户表现出对你个人的好奇\n- 用户愿意接受你的下一步安排\n\n输入占位符\n- 用户问题：{sys_query}\n- 共情话术库参考：{{#context#}}\n\n任务要求\n1. 你必须只输出 JSON，不能包含任何多余的文字。\n\n示例输出：\n{\n  \"message\": \"It sounds like you're going through a tough moment, and I’m here to support you.\",\n  \"reason\": \"用户在表达负面情绪，没有表现出购买意向，因此需要继续共情。\",\n  \"tag\": \"继续共情\"\n}")
.params(Map.of("sys_query", "null"))
.chatClient(chatClient)
.outputKey("LLMNode15_text")
.build();
stateGraph.addNode("LLMNode15", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode15.apply(state);
	String key = "LLMNode15_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— CodeNode [1757651036593] ——
CodeExecutorNodeAction codeNode15 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: list[dict]) -> dict:
    quotable_products = [
        item["productName"] for item in arg1 if item.get("isQuotable") == 1
    ]

    return {"quotableProductNames": quotable_products}

""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "conversation_collect_product_info"))
.outputKey("codeNode15_output")
    .build();
stateGraph.addNode("codeNode15", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode15.apply(state);
            String key = "codeNode15_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode15_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— BranchNode [1757657087454] ——
stateGraph.addNode("branchNode5", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1757677324196] ——
LlmNode LLMNode16 = LlmNode.builder()
.systemPromptTemplate("You are a product information assistant.\n\nInput:\n- Customer product info: {conversation_collect_product_info_str}\n- Reference sales script: {LLMNode17_text}\n- Merchant rules: {codeNode3_general_rules_str}\n- Product database links: {{#context#}}\n\nTask:\n1. Only ask about customer info fields that are empty, in order.\n2. Skip attributes already provided; do not repeat questions.\n3. Provide at least 5 relevant product links, each ending with ###.\n4. Reply in English, concise (<30 words, links not counted), direct.\n5. Core question/info in the first sentence.\n6. End each sentence with . ? ! and add ### after each.\n7. Do not provide any price or quotation.\n8. Follow merchant rules strictly.\n\nProhibitions:\n- >40 words (links not counted)\n- Incomplete sentences\n- Consecutive questions\n- Repeated or similar meanings")
.params(Map.of("codeNode3_general_rules_str", "null", "conversation_collect_product_info_str", "null", "LLMNode17_text", "null"))
.chatClient(chatClient)
.outputKey("LLMNode16_text")
.build();
stateGraph.addNode("LLMNode16", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode16.apply(state);
	String key = "LLMNode16_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— BranchNode [1758018112991] ——
stateGraph.addNode("branchNode6", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1758189714741] ——
LlmNode LLMNode17 = LlmNode.builder()
.systemPromptTemplate("你是一个专业的【销售报价话术整合助手】。\n目标：基于提供的销售思维与报价话术，结合用户的提问或反馈，生成合适的销售回复。\n输入信息：\n报价话术：{{#context#}}\n用户输入：{{#user_input#}}\n输出要求：\n自然融合报价话术，语言要顺畅且有说服力。\n语气保持：专业、真诚、积极引导成交。\n既要满足客户疑问，又要塑造价值，推动成交。\n回复简洁有力（少于40字），避免冗长。")
.chatClient(chatClient)
.outputKey("LLMNode17_text")
.build();
stateGraph.addNode("LLMNode17", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode17.apply(state);
	String key = "LLMNode17_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— VariableAggregatorNode [1758191872887] ——
VariableAggregatorNode variableAggregatorNode1 = VariableAggregatorNode.builder()
    .variables(List.of(List.of("codeNode16_result"), List.of("LLMNode13_text"), List.of("LLMNode16_text"), List.of("LLMNode6_text")))
    .outputKey("variableAggregatorNode1_output")
    .outputType("list")
    .build();
stateGraph.addNode("variableAggregatorNode1", AsyncNodeAction.node_async((state) -> {
	Map<String, Object> result = variableAggregatorNode1.apply(state);
	String key = "variableAggregatorNode1_output";
	Object object = result.get(key);
	if (object instanceof List<?> list) {
		return Map.of(key, list.isEmpty() ? "unknown" : list.get(0));
	} else {
		return Map.of(key, object == null ? "unknown" : object.toString());
	}
}
));

// —— AnswerNode [1758191904312] ——
AnswerNode answerNode2 = AnswerNode.builder()
.answer("{variableAggregatorNode1_output}")
.outputKey("answerNode2_answer")
.build();
stateGraph.addNode("answerNode2", AsyncNodeAction.node_async(answerNode2));

stateGraph.addEdge("answerNode2", END);
// —— CodeNode [17581921212590] ——
CodeExecutorNodeAction codeNode16 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""

def main(arg1: dict) -> dict:
    return {
        "result": arg1.get("message", ""),
    }

""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "variableAggregatorNode2_output"))
.outputKey("codeNode16_output")
    .build();
stateGraph.addNode("codeNode16", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode16.apply(state);
            String key = "codeNode16_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode16_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— VariableAggregatorNode [1758194021802] ——
VariableAggregatorNode variableAggregatorNode2 = VariableAggregatorNode.builder()
    .variables(List.of(List.of("LLMNode15_structured_output")))
    .outputKey("variableAggregatorNode2_output")
    .outputType("list")
    .build();
stateGraph.addNode("variableAggregatorNode2", AsyncNodeAction.node_async((state) -> {
	Map<String, Object> result = variableAggregatorNode2.apply(state);
	String key = "variableAggregatorNode2_output";
	Object object = result.get(key);
	if (object instanceof List<?> list) {
		return Map.of(key, list.isEmpty() ? "unknown" : list.get(0));
	} else {
		return Map.of(key, object == null ? "unknown" : object.toString());
	}
}
));

// —— AssignerNode [1758200967258] ——
AssignerNode assignerNode4 = AssignerNode.builder()
.addItem("conversation_sceneId", null, AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode4", AsyncNodeAction.node_async(assignerNode4));

// —— LlmNode [17582503748420] ——
LlmNode LLMNode18 = LlmNode.builder()
.systemPromptTemplate("任务：根据商品信息与图片描述生成英文、单行、小写、**中横线分隔（kebab-case）**的搜索查询 slug，仅输出最终 slug，不要解释或多余内容。\n\n输入：\n- 商品信息：{conversation_collect_product_info_str}\n- 图片描述：{conversation_image_analysis}\n- 在售商品：{codeNode4_product_names_list}\n\n规则：\n1. 中文翻译为英文。\n2. 基础词取自 productName 核心名词，空格转中横线。\n3. 按 attributes 顺序追加非空 value（翻译为英文），单位规范化（如 \"2 inch\"→\"2inch\"、\"10 mm\"→\"10mm\"）。\n4. 可从图片描述提取 1–2 个英文关键词补充。\n5. 输出仅一条 slug，字符仅限 a–z、0–9、-。\n6. 若 attributes 值全为空或无商品信息，从在售商品随机选英文名称生成 slug。\n\n示例（仅演示格式，不限品类）：\n - 商品信息:\n{\"productName\": \"古巴链cuban chain\", \"attributes\": [{\"label\": \"尺寸-宽度（mm）\", \"description\": \"宽度（mm）\", \"value\": \"2\"}, {\"label\": \"尺寸-长度（inch）\", \"description\": \"长度（inch）\", \"value\": \"22\"}]}\n输出: cuban-chain-2mm-22inch\n - 商品信息:\n{\"productName\": \"玫瑰金定制字母Y吊坠\", \"attributes\": []}\n图片描述含 \"custom letter y pendant\"\n输出: rose-gold-letter-y-pendant\n - 商品信息:\n{\"productName\": \"红色运动鞋\", \"attributes\": [{\"label\": \"尺码\", \"description\": \"尺码\", \"value\": \"42\"}]}\n输出: sneakers-red-42\n - 商品信息:\n{\"productName\": \"实木餐桌\", \"attributes\": [{\"label\": \"尺寸-长度（米）\", \"description\": \"长度（米）\", \"value\": \"2\"}]}\n输出: wooden-dining-table-2m")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode4_product_names_list", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode18_text")
.build();
stateGraph.addNode("LLMNode18", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode18.apply(state);
	String key = "LLMNode18_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [17582503774360] ——
KnowledgeRetrievalNode knowledgeRetrievalNode8 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode18_text")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-large")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode8_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode8", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode8_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode8.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [17582503829810] ——
LlmNode LLMNode19 = LlmNode.builder()
.systemPromptTemplate("任务：根据商品信息和图片描述，生成中文关键词列表，使用“、”分隔，仅输出关键词，不含解释或多余内容。\n\n输入：\n - 商品信息：{conversation_collect_product_info_str}\n - 图片描述：{conversation_image_analysis}\n - 在售商品：{codeNode4_product_names_list}\n\n规则：\n1. 英文转中文。\n2. 从 productName 提取核心名词，空格转“、”。\n3. 按 attributes 顺序追加非空 value（中文翻译），单位转英文缩写（如 \"2 inch\"→\"2inch\"、\"10 mm\"→\"10mm\"）。\n4. 可从图片描述提取 1–2 个中文关键词补充。\n5. 输出仅一行关键词列表，使用“、”分隔。\n6. 若 attributes 值全为空或无商品信息，从在售商品随机选中文名称生成。\n\n示例（仅演示格式，不限品类）：\n- 商品信息: {\"productName\": \"古巴链cuban chain\", \"attributes\": [{\"label\": \"尺寸-宽度（mm）\", \"description\": \"宽度（mm）\", \"value\": \"2\"}, {\"label\": \"尺寸-长度（inch）\", \"description\": \"长度（inch）\", \"value\": \"22\"}]} \n输出: 古巴链、2mm、22inch\n- 商品信息: {\"productName\": \"玫瑰金定制字母Y吊坠\", \"attributes\": []} 图片描述含 \"custom letter y pendant\" \n输出: 玫瑰金、定制、字母吊坠、Yt\n- 商品信息: {\"productName\": \"红色运动鞋\", \"attributes\": [{\"label\": \"尺码\", \"description\": \"尺码\", \"value\": \"42\"}]} \n输出: 运动鞋、红色、42码\n- 商品信息: {\"productName\": \"实木餐桌\", \"attributes\": [{\"label\": \"尺寸-长度（米）\", \"description\": \"长度（米）\", \"value\": \"2\"}]} \n输出: 实木餐桌、2米")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode4_product_names_list", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode19_text")
.build();
stateGraph.addNode("LLMNode19", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode19.apply(state);
	String key = "LLMNode19_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [17582504454200] ——
KnowledgeRetrievalNode knowledgeRetrievalNode9 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode19_text")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-large")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode9_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode9", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode9_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode9.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— CodeNode [1758250743153] ——
CodeExecutorNodeAction codeNode17 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
import re 

def main(arg1: list[dict], arg2: list[dict]) -> dict:
    threshold = 0.15

    merged = arg1 + arg2

    filtered_sorted = sorted(
        [
            item
            for item in merged
            if item.get("metadata", {}).get("score", 0) >= threshold
        ],
        key=lambda x: x.get("metadata", {}).get("score", 0),
        reverse=True,
    )

    result_count = len(filtered_sorted)

    url_pattern = re.compile(r'https?://[^\\s"\\']+')
    links = []
    for item in filtered_sorted:
        content = item.get("content", "")
        matches = url_pattern.findall(content)
        if matches:
            links.extend(matches)

    return {
        "result": filtered_sorted,
        "count": result_count,
        "links": list(set(links)),
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "knowledgeRetrievalNode2_result", "arg2", "knowledgeRetrievalNode1_result"))
.outputKey("codeNode17_output")
    .build();
stateGraph.addNode("codeNode17", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode17.apply(state);
            String key = "codeNode17_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode17_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [17582512538170] ——
CodeExecutorNodeAction codeNode18 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
import re 

def main(arg1: list[dict], arg2: list[dict]) -> dict:
    threshold = 0.15

    merged = arg1 + arg2

    filtered_sorted = sorted(
        [
            item
            for item in merged
            if item.get("metadata", {}).get("score", 0) >= threshold
        ],
        key=lambda x: x.get("metadata", {}).get("score", 0),
        reverse=True,
    )

    result_count = len(filtered_sorted)

    url_pattern = re.compile(r'https?://[^\\s"\\']+')
    links = []
    for item in filtered_sorted:
        content = item.get("content", "")
        matches = url_pattern.findall(content)
        if matches:
            links.extend(matches)

    return {
        "result": filtered_sorted,
        "count": result_count,
        "links": list(set(links)),
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "knowledgeRetrievalNode8_result", "arg2", "knowledgeRetrievalNode9_result"))
.outputKey("codeNode18_output")
    .build();
stateGraph.addNode("codeNode18", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode18.apply(state);
            String key = "codeNode18_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode18_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— QuestionClassifierNode [1758281118514] ——
QuestionClassifierNode questionClassifyNode1 = QuestionClassifierNode.builder()
.chatClient(chatClient)
.inputTextKey("sys_query")
.categories(List.of("共情\n用户消息与商品或销售完全无关，需要共情回应，例如闲聊、表达情绪、打招呼\n\n", "报价\n用户明确询问价格", "商品收集\n涉及到购买"))
.outputKey("questionClassifyNode1_class_name")
.classificationInstructions(List.of("请根据输入内容选择对应分类"))
.build();
stateGraph.addNode("questionClassifyNode1", AsyncNodeAction.node_async(questionClassifyNode1));

// —— BranchNode [1758533404003] ——
stateGraph.addNode("branchNode7", AsyncNodeAction.node_async(state -> Map.of()));

// —— AssignerNode [1758534107575] ——
AssignerNode assignerNode5 = AssignerNode.builder()
.addItem("conversation_sceneId", "codeNode14_id", AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode5", AsyncNodeAction.node_async(assignerNode5));

// —— BranchNode [1758536584155] ——
stateGraph.addNode("branchNode8", AsyncNodeAction.node_async(state -> Map.of()));

// —— CodeNode [1758794070337] ——
CodeExecutorNodeAction codeNode19 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
import random


def main(arg1: list[dict]):
    result = []
    for product in arg1:
        empty_attrs = [
            attr["description"]
            for attr in product.get("attributes", [])
            if attr.get("value") == ""
        ]
        if empty_attrs:
            result.append(random.choice(empty_attrs))
    return {"description": result}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "conversation_collect_product_info"))
.outputKey("codeNode19_output")
    .build();
stateGraph.addNode("codeNode19", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode19.apply(state);
            String key = "codeNode19_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode19_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— LlmNode [1758878074824] ——
LlmNode LLMNode20 = LlmNode.builder()
.systemPromptTemplate("任务：根据客户商品信息与知识库结果，推荐 5–10 条相关商品链接。\n\n规则：\n输入\n客户商品信息：{conversation_collect_product_info_str}\n知识库检索结果：{{#context#}}\n\n推荐逻辑\n如果客户商品信息为空 → 根据知识库检索结果推荐 5–10 条相关商品链接，尽量覆盖不同相关类别。\n如果客户商品信息不为空 →\n  a. value 不为空 → 根据 value 匹配所有合适的商品链接，推荐 5–10 条。只要类别相关（例如：客户询问productname → 所有productname相关商品都推荐）。\n  b. value 为空 → 推荐 5–10 条相关商品链接。\n\n输出要求\n必须输出 5–10 条可点击商品链接，优先按相关度排序。\n如果符合条件的商品少于 10 条 → 输出所有符合条件的商品。\n单条或两条结果不可接受。\n若无合适商品 → 说明原因并提供 5–10 条最接近的替代链接。")
.params(Map.of("conversation_collect_product_info_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode20_text")
.build();
stateGraph.addNode("LLMNode20", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode20.apply(state);
	String key = "LLMNode20_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— CodeNode [1758881956387] ——
CodeExecutorNodeAction codeNode20 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str):
    if not arg1.strip():
        return {"result": []}

    try:
        data = json.loads(arg1)
        if isinstance(data, list):
            return {"result": data}
        else:
            return {"result": []}
    except json.JSONDecodeError:
        return {"result": []}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "startNode1_history"))
.outputKey("codeNode20_output")
    .build();
stateGraph.addNode("codeNode20", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode20.apply(state);
            String key = "codeNode20_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode20_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));


        // add edges
        stateGraph.addEdge("httpNode1", "codeNode1");
stateGraph.addEdge("httpNode2", "codeNode2");
stateGraph.addEdge("httpNode3", "codeNode3");
stateGraph.addEdge("httpNode4", "codeNode4");
stateGraph.addEdge("httpNode5", "codeNode5");
stateGraph.addEdge("httpNode8", "LLMNode3");
stateGraph.addEdge(START, "httpNode1");
stateGraph.addEdge("httpNode7", "codeNode6");
stateGraph.addEdge("LLMNode3", "codeNode7");
stateGraph.addEdge("codeNode7", "httpNode9");
stateGraph.addEdge("codeNode10", "branchNode2");
stateGraph.addEdge("LLMNode7", "answerNode1");
stateGraph.addEdge("LLMNode11", "knowledgeRetrievalNode3");
stateGraph.addEdge("httpNode10", "codeNode11");
stateGraph.addEdge("codeNode11", "branchNode1");
stateGraph.addEdge("LLMNode8", "assignerNode2");
stateGraph.addEdge("LLMNode4", "codeNode9");
stateGraph.addEdge("LLMNode2", "codeNode12");
stateGraph.addEdge("codeNode12", "httpNode8");
stateGraph.addEdge("knowledgeRetrievalNode3", "codeNode13");
stateGraph.addEdge("LLMNode10", "knowledgeRetrievalNode2");
stateGraph.addEdge("LLMNode9", "knowledgeRetrievalNode1");
stateGraph.addEdge("answerNode1", "httpNode6");
stateGraph.addEdge("LLMNode1", "codeNode14");
stateGraph.addEdge("assignerNode3", "LLMNode7");
stateGraph.addEdge("codeNode15", "branchNode5");
stateGraph.addEdge("LLMNode5", "codeNode8");
stateGraph.addEdge("codeNode8", "LLMNode4");
stateGraph.addEdge("codeNode9", "assignerNode1");
stateGraph.addEdge("assignerNode2", "LLMNode5");
stateGraph.addEdge("knowledgeRetrievalNode7", "LLMNode15");
stateGraph.addEdge("knowledgeRetrievalNode5", "LLMNode13");
stateGraph.addEdge("knowledgeRetrievalNode6", "LLMNode14");
stateGraph.addEdge("LLMNode12", "knowledgeRetrievalNode5");
stateGraph.addEdge("knowledgeRetrievalNode4", "LLMNode17");
stateGraph.addEdge("LLMNode13", "variableAggregatorNode1");
stateGraph.addEdge("LLMNode16", "variableAggregatorNode1");
stateGraph.addEdge("LLMNode6", "variableAggregatorNode1");
stateGraph.addEdge("codeNode16", "variableAggregatorNode1");
stateGraph.addEdge("variableAggregatorNode1", "codeNode10");
stateGraph.addEdge("answerNode2", "branchNode4");
stateGraph.addEdge("LLMNode15", "branchNode6");
stateGraph.addEdge("variableAggregatorNode2", "codeNode16");
stateGraph.addEdge("httpNode6", "assignerNode4");
stateGraph.addEdge("assignerNode4", "LLMNode2");
stateGraph.addEdge("LLMNode18", "knowledgeRetrievalNode8");
stateGraph.addEdge("LLMNode19", "knowledgeRetrievalNode9");
stateGraph.addEdge("knowledgeRetrievalNode2", "codeNode17");
stateGraph.addEdge("knowledgeRetrievalNode9", "codeNode18");
stateGraph.addEdge("codeNode14", "assignerNode5");
stateGraph.addEdge("assignerNode5", "branchNode7");
stateGraph.addEdge("codeNode18", "branchNode8");
stateGraph.addEdge("assignerNode1", "codeNode19");
stateGraph.addEdge("codeNode17", "LLMNode20");
stateGraph.addEdge("LLMNode20", "LLMNode6");
stateGraph.addEdge("codeNode20", "LLMNode2");
stateGraph.addEdge("codeNode1", "httpNode2");
stateGraph.addEdge("codeNode2", "httpNode3");
stateGraph.addEdge("codeNode3", "httpNode4");
stateGraph.addEdge("codeNode4", "httpNode5");
stateGraph.addEdge("codeNode5", "httpNode7");
stateGraph.addEdge("codeNode6", "httpNode10");
stateGraph.addEdge("codeNode19", "questionClassifyNode1");
stateGraph.addEdge("codeNode13", "branchNode3");
stateGraph.addEdge("LLMNode14", "LLMNode9");
stateGraph.addEdge("knowledgeRetrievalNode1", "LLMNode10");
stateGraph.addEdge("LLMNode17", "LLMNode18");
stateGraph.addEdge("knowledgeRetrievalNode8", "LLMNode19");
stateGraph.addConditionalEdges("branchNode8", edge_async(state -> {
if(!(state.value("codeNode18_count", java.lang.String.class).orElseThrow().isEmpty())) {
return "true";}
return "false";}), Map.of("true", "LLMNode16"));
stateGraph.addConditionalEdges("branchNode7", edge_async(state -> {
if((state.value("codeNode14_id", String.class).orElseThrow().equals("1"))) {
return "true";}
return "false";}), Map.of("true", "httpNode6", "false", "LLMNode11"));
stateGraph.addConditionalEdges("questionClassifyNode1",
            edge_async(state -> {
String value = state.value("questionClassifyNode1_class_name", String.class).orElse("");
if (value.contains("报价 用户明确询问价格")) return "报价 用户明确询问价格";
if (value.contains("共情 用户消息与商品或销售完全无关，需要共情回应，例如闲聊、表达情绪、打招呼 "))
    return "共情 用户消息与商品或销售完全无关，需要共情回应，例如闲聊、表达情绪、打招呼 ";
if (value.contains("商品收集 涉及到购买")) return "商品收集 涉及到购买";
    return "商品收集 涉及到购买";}),
            Map.of("报价 用户明确询问价格", "codeNode15",
                    "共情 用户消息与商品或销售完全无关，需要共情回应，例如闲聊、表达情绪、打招呼 ", "knowledgeRetrievalNode7",
                    "商品收集 涉及到购买", "knowledgeRetrievalNode6")
);
stateGraph.addConditionalEdges("branchNode1", edge_async(state -> {
if((state.value("unknown", String.class).orElseThrow().equals("开始进行聊天分析"))) {
return "971d3615-1547-4353-93b4-8e584059f8a5";}
return "false";}), Map.of("971d3615-1547-4353-93b4-8e584059f8a5", "codeNode20", "false", "LLMNode1"));
stateGraph.addConditionalEdges("branchNode6", edge_async(state -> {
if((state.value("LLMNode15_text", java.lang.String.class).orElseThrow().equals("推进销售"))) {
return "true";}
return "false";}), Map.of("true", "knowledgeRetrievalNode6", "false", "variableAggregatorNode2"));
stateGraph.addConditionalEdges("branchNode2", edge_async(state -> {
if((state.value("codeNode10_result", java.lang.String.class).orElseThrow().isEmpty())) {
return "true";}
return "false";}), Map.of("true", "answerNode2", "false", "assignerNode3"));
stateGraph.addConditionalEdges("branchNode4", edge_async(state -> {
if(!(state.value("unknown", String.class).orElseThrow().isEmpty())) {
return "true";}
return "false";}), Map.of("true", "httpNode6"));
stateGraph.addConditionalEdges("branchNode3", edge_async(state -> {
if(!(state.value("unknown", java.lang.String.class).orElseThrow().isEmpty())) {
return "true";}
return "false";}), Map.of("false", "LLMNode5", "true", "LLMNode8"));
stateGraph.addConditionalEdges("branchNode5", edge_async(state -> {
if(!(state.value("codeNode15_quotableProductNames", java.lang.String.class).orElseThrow().isEmpty())) {
return "true";}
return "false";}), Map.of("true", "LLMNode12", "false", "knowledgeRetrievalNode4"));
//stateGraph;

        return stateGraph.compile();
    }

    @Bean
    public VectorStore customVectorStore(EmbeddingModel embeddingModel) {
        var chunks = new TokenTextSplitter().transform(new TextReader(ragSource).read());
        SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build();
        vectorStore.write(chunks);
        return vectorStore;
    }

    @Bean
    public Path tempDir() throws IOException {
        // todo： set your work dir
        Path tempDir = Files.createTempDirectory("code-execution-workdir-");
        tempDir.toFile().deleteOnExit();
        return tempDir;
    }

    @Bean
    public CodeExecutionConfig codeExecutionConfig(Path tempDir) {
        return new CodeExecutionConfig().setWorkDir(tempDir.toString());
    }

    @Bean
    public CodeExecutor codeGenerator() {
        return new LocalCommandlineCodeExecutor();
    }
}
